﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
using Common.Constants;
using Common.Helper;
using EntityFrameworkExtension.Helper;
using EntityFrameworkExtension.Interface;
using MainProcessor.Interface;

namespace MainProcessor.ContextProcessor
{
    public abstract class BaseContextProcessor<T> where T : class 
    {
        private readonly IDataController dataController;
        public IEfContext EfContext { get; set; }

        protected List<T> List { get; set; }
        protected bool DataChanged = false;

        protected int MappingId { get; set; }

        protected BaseContextProcessor(IDataController dataController)
        {
            this.dataController = dataController;
        }

        public virtual IEnumerable<Guid> GetGuids()
        {
            return Enumerable.Empty<Guid>();
        }

        public virtual bool HasChanges()
        {
            return DataChanged;
        }

        protected Guid GetGuid(int id)
        {
            return dataController.GetGuidIntMappings1(id, EfContext);
        }


        protected void Populate(T input, IEfContext efContext) 
        {
            Generic.Populate(input, SelectCurrent, Insert, Update, efContext);
        }

        protected virtual void Update(T existing, T target)
        {
            foreach (PropertyInfo property in existing.GetType().GetProperties())
            {
                PropertyInfo targetProperty = target.GetType().GetProperty(property.Name);

                if (property.CanWrite && property.Name.ToLower() != "id" && property.Name != "EntityKey" && property.Name.ToLower() != "LastModifiedOn")
                    property.SetValue(existing, Update(property.GetValue(existing, null), targetProperty.GetValue(target, null)), null);
            }

        }

        protected virtual void Insert(T newItem, IEfContext efContext)
        {
            efContext.AddToObjectList(newItem);
        }

        protected virtual void Delete(T newItem, IEfContext efContext)
        {
            efContext.Delete(newItem);
        }

        protected virtual T SelectCurrent(T inputFund, IEfContext efContext)
        {
            return default(T);
        }

        public static TU? NullableValue<TU>(XElement input) where TU : struct
        {
            var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(TU?));

            if (input == null || converter == null)
                return null;
            return (TU?)converter.ConvertFrom(input.Value);

        }
        
        protected static TU Update<TU>(TU current, TU input) 
        {
            return input;
        }

        

        
        
        protected void DelSert<TU>(IEfContext efContext, IEqualityComparer<T> equalityComparer, Func<T, TU> groupFilter, Func<TU, IEfContext, IEnumerable<T>> getExisting)
        {
            ProcessGrud(efContext, equalityComparer, groupFilter, getExisting,
                 new List<Action<IEnumerable<T>, IEnumerable<T>, IEfContext, IEqualityComparer<T>>> { Insert, Delete });
        }
        
        protected void Insert<TU>(IEfContext efContext, IEqualityComparer<T> equalityComparer, Func<T, TU> groupFilter, Func<TU, IEfContext, IEnumerable<T>> getExisting)
        {
            ProcessGrud(efContext, equalityComparer, groupFilter, getExisting,
               new List<Action<IEnumerable<T>, IEnumerable<T>, IEfContext, IEqualityComparer<T>>> { Insert });
        }


        protected void UpSert<TU>(IEfContext efContext, IEqualityComparer<T> equalityComparer, Func<T, TU> groupFilter, Func<TU, IEfContext, IEnumerable<T>> getExisting)
        {
            ProcessGrud(efContext, equalityComparer, groupFilter, getExisting,
                 new List<Action<IEnumerable<T>, IEnumerable<T>, IEfContext, IEqualityComparer<T>>> { Update, Insert });
        }
        
        protected void UpDelSert<TU>(IEfContext efContext, IEqualityComparer<T> equalityComparer, Func<T, TU> groupFilter, Func<TU, IEfContext, IEnumerable<T>> getExisting)
        {
            ProcessGrud(efContext, equalityComparer, groupFilter, getExisting,
                 new List<Action<IEnumerable<T>, IEnumerable<T>, IEfContext, IEqualityComparer<T>>> { Update, Insert, Delete });   
        }
        
        private void Delete(IEnumerable<T> current, IEnumerable<T> existing, IEfContext efContext, IEqualityComparer<T> equalityComparer)
        {
            var toDelete = existing.Except(current, equalityComparer);
            toDelete.ToList().ForEach(x => Delete(x, efContext));
        }



        private void ProcessGrud<TU>(IEfContext efContext, IEqualityComparer<T> equalityComparer, Func<T, TU> groupFilter, Func<TU,
            IEfContext, IEnumerable<T>> getExisting, List<Action<IEnumerable<T>, IEnumerable<T>, IEfContext, IEqualityComparer<T>>> grud)
        {
            var grouped = List.GroupBy(groupFilter).ToLookup(y => y.Key);

            foreach (var currentGroup in grouped)
            {
                TU guid1 = currentGroup.Key;
                var existing = getExisting(guid1, efContext);
                var current = currentGroup.SelectMany(y => y);

                grud.ForEach(x => x(current, existing, efContext, equalityComparer));
            }
        }
        
        private void Update(IEnumerable<T> current, IEnumerable<T> existing, IEfContext efContext, IEqualityComparer<T> equalityComparer)
        {
            var toUpdate = current.Intersect(existing, equalityComparer);
            toUpdate.ToList().ForEach(x => Update(existing.Single(item => equalityComparer.Equals(x, item)), x));
        }

        private void Insert(IEnumerable<T> current, IEnumerable<T> existing,IEfContext efContext, IEqualityComparer<T> equalityComparer)
        {
            var toInsert = current.Except(existing, equalityComparer);
            toInsert.ToList().ForEach(x => Insert(x, efContext));
        }

        
    }
}
